//------------------------------------------------------------------------------
// <copyright file="UpdateControl.xaml.cs" company="Damian Antonowicz">
//       copyright © 2010 Damian Antonowicz
// </copyright>
// <author>Damian Antonowicz</author>
// <email>poczta@damianantonowicz.pl</email>
//------------------------------------------------------------------------------

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using DAnton.Utilities.Extensions;
using DAnton.Blocks.Updater;
using System.Reflection;
using DotBeer.Data.Properties;
using System.Windows.Media.Effects;
using System.ComponentModel;
using System.Security.Cryptography;
using DotBeer.Business.Entities.Updates;
using System.Threading;
using System.Windows.Threading;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using DotBeer.Business.Components;
using DotBeer.Business.Components.Exceptions;

namespace DotBeer.UI.WPFClient
{
    /// <summary>
    /// Interaction logic for UpdateControl.xaml
    /// </summary>
    public partial class UpdateControl : UserControl
    {
        #region private members

        AppTextExceptionFormatter exceptionFormatter;

        ApplicationUpdaterBlock updater;
        TextBlock downloadingTextBlockBytes;
        TextBlock downloadingTextBlockPercentage;

        Storyboard showControlStoryboard,
                   hideControlStoryboard;

        #endregion

        #region constructors

        public UpdateControl()
        {
            InitializeComponent();

            this.exceptionFormatter = new AppTextExceptionFormatter();

            this.BorderRoot.Opacity = 0;

            this.showControlStoryboard = (Storyboard)FindResource("ShowControl");
            this.hideControlStoryboard = (Storyboard)FindResource("HideControl");

            this.updater = new ApplicationUpdaterBlock(new SHA256Managed(),
                                                       new DotBeerUpdatesDownloader(),
                                                       new DotBeerUpdatesInfoProvider());
        }

        #endregion

        #region properties

        public bool ShowConnectingMessage { get; set; }
        public bool CheckUpdatesAtLoaded { get; set; }
        public bool ShowDropShadow { get; set; }

        public bool IsBusy
        {
            get { return this.updater.IsBusy; }
        }

        #endregion

        #region public methods

        public void CheckUpdate()
        {
            this.updater.CheckUpdatesCompleted += new EventHandler<CheckUpdatesCompletedEventArgs>(updater_CheckUpdatesCompleted);
            this.updater.CheckUpdatesProgressChanged += new EventHandler<ProgressChangedEventArgs>(updater_CheckUpdatesProgressChanged);

            if (this.ShowConnectingMessage)
            {
                this.showControlStoryboard.Begin();
                this.InvokeIfNeeded(this.AddCheckingUpdatesControls);
            }

            this.updater.CheckUpdatesAsync(Assembly.GetExecutingAssembly().GetName().Version, "DotBeer");
        }

        #endregion

        #region private methods

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.ShowDropShadow)
                this.BorderRoot.Effect = new DropShadowEffect();

            if (this.CheckUpdatesAtLoaded && UpdateSettings.Default.CheckAtStartup)
                this.CheckUpdate();
        }

        void updater_CheckUpdatesProgressChanged(object sender, ProgressChangedEventArgs e)
        {
        }

        void updater_CheckUpdatesCompleted(object sender, CheckUpdatesCompletedEventArgs e)
        {
            this.updater.CheckUpdatesCompleted -= this.updater_CheckUpdatesCompleted;
            this.updater.CheckUpdatesProgressChanged -= this.updater_CheckUpdatesProgressChanged;

            this.ClearLayoutRoot();

            if (e.Cancelled)
            {
                this.AddMessageControls("Checking updates has been cancelled.");
            }
            else if (e.Error != null)
            {
                this.ShowError(e.Error, "Error occured while checking updates:");
            }
            else
            {
                if (e.UpdatesAvailable)
                {
                    if (!this.ShowConnectingMessage)
                        this.showControlStoryboard.Begin();

                    this.AddUpdatesAvailableControls(e.LatestVersion);
                }
                else
                    this.AddUpdatesNotAvailableControls();
            }
        }

        private void updater_DownloadUpdatesProgressChanged(object sender, DownloadUpdatesProgressChangedEventArgs e)
        {
            float megabytesReader = (float)e.BytesReaded / 1048576;
            
            this.downloadingTextBlockPercentage.Text = String.Format("Completed: {0}% {1} Transfer rate: {2} kb/s",
                                                                     e.ProgressPercentage, "".PadLeft(10, ' '), e.TransferRate);
            this.downloadingTextBlockBytes.Text = String.Format("{0:f2} MB ({1} bytes) of {2:f2} MB ({3} bytes)",
                                                                megabytesReader,
                                                                e.BytesReaded,
                                                                e.TotalMegaBytes,
                                                                e.TotalBytes);
        }

        private void updater_DownloadUpdatesCompleted(object sender, DownloadUpdatesCompletedEventArgs e)
        {
            this.updater.DownloadUpdatesCompleted -= this.updater_DownloadUpdatesCompleted;
            this.updater.DownloadUpdatesProgressChanged -= this.updater_DownloadUpdatesProgressChanged;
            this.ClearLayoutRoot();

            if (e.Cancelled)
            {
                this.AddMessageControls("Downloading updates has been cancelled.");
            }
            else if (e.Error != null)
            {
                this.ShowError(e.Error, "Error occured while downloading updates:");
            }
            else
            {
                this.AddDownloadCompletedControls(e.IsUpdaterAlreadyDownloaded);
            }
        }

        private void ShowError(Exception exception, string message)
        {
            Exception replacedException;
            HandleExceptions.UpdatePolicy(exception, out replacedException);

            this.AddMessageControls(String.Format("{0}\n{1}",
                                    message,
                                    this.exceptionFormatter.Format(replacedException)));
        }

        private void ClearLayoutRoot()
        {
            this.LayoutRoot.Children.Clear();
        }

        private void AddCheckingUpdatesControls()
        {
            this.ClearLayoutRoot();
            this.LayoutRoot.Children.Add(GetTextBlock("Connecting to server...", 0));

            Button buttonCancel = this.GetButton("Cancel", 1);
            buttonCancel.HorizontalAlignment = HorizontalAlignment.Center;
            buttonCancel.VerticalAlignment = VerticalAlignment.Center;
            buttonCancel.Click += (object sender, RoutedEventArgs e) =>
                {
                    this.updater.CancelAsync();
                };

            this.LayoutRoot.Children.Add(buttonCancel);
        }

        private void AddUpdatesNotAvailableControls()
        {
            this.ClearLayoutRoot();
            this.LayoutRoot.Children.Add(GetTextBlock("DotBeer is up to date.", 0));
        }

        private void AddUpdatesAvailableControls(Version latestVersion)
        {
            this.ClearLayoutRoot();

            TextBlock textBlock = GetTextBlock(String.Format("New DotBeer version: {0} is available. Download now?", latestVersion.ToString()), 0);
            StackPanel panel = GetStackPanel();

            Button buttonYes = GetButton("Yes", 1);
            buttonYes.Click += delegate
                                        {
                                            this.AddDownloadingControls();
                                            this.updater.DownloadUpdatesCompleted += new EventHandler<DownloadUpdatesCompletedEventArgs>(updater_DownloadUpdatesCompleted);
                                            this.updater.DownloadUpdatesProgressChanged += new EventHandler<DownloadUpdatesProgressChangedEventArgs>(updater_DownloadUpdatesProgressChanged);
                                            this.updater.DownloadUpdatesAsync();
                                        };

            Button buttonNo = GetButton("No", 1);
            buttonNo.Click += delegate { this.HideControl(); };

            panel.Children.Add(buttonYes);
            panel.Children.Add(buttonNo);

            buttonYes.Focus();

            this.LayoutRoot.Children.Add(textBlock);
            this.LayoutRoot.Children.Add(panel);
        }

        private void AddDownloadingControls()
        {
            this.ClearLayoutRoot();

            TextBlock textBlock = GetTextBlock("Downloading...", 0);
            textBlock.HorizontalAlignment = HorizontalAlignment.Left;
            textBlock.Margin = new Thickness(10, 0, 0, 0);

            Button buttonCancel = this.GetButton("Cancel", 0);
            buttonCancel.HorizontalAlignment = HorizontalAlignment.Right;
            buttonCancel.VerticalAlignment = VerticalAlignment.Center;
            buttonCancel.Margin = new Thickness(0, 0, 10, 0);
            buttonCancel.Click += (object sender, RoutedEventArgs e) =>
                {
                    this.updater.CancelAsync();
                };

            this.downloadingTextBlockPercentage = GetTextBlock("", 1);
            this.downloadingTextBlockPercentage.Margin = new Thickness(10, 10, 10, 30);

            this.downloadingTextBlockBytes = GetTextBlock("", 1);
            this.downloadingTextBlockBytes.Margin = new Thickness(0, 10, 0, 0);

            this.LayoutRoot.Children.Add(textBlock);
            this.LayoutRoot.Children.Add(this.downloadingTextBlockPercentage);
            this.LayoutRoot.Children.Add(this.downloadingTextBlockBytes);
            this.LayoutRoot.Children.Add(buttonCancel);
        }

        private void AddDownloadCompletedControls(bool updaterIsAlreadyDownloaded)
        {
            this.ClearLayoutRoot();

            TextBlock textBlock = null;

            if (!updaterIsAlreadyDownloaded)
                textBlock = this.GetTextBlock("Download completed.", 0);
            else
                textBlock = this.GetTextBlock("Updater is already downloaded.", 0);

            StackPanel panel = GetStackPanel();

            Button buttonUpdateNow = GetButton("Update now", 1);
            buttonUpdateNow.Click += delegate
            {
                Application.Current.Shutdown();
                this.updater.RunUpdater();
            };

            Button buttonUpdateLater = GetButton("Update later", 1);
            buttonUpdateLater.Click += delegate
            {
                this.HideControl();
            };

            panel.Children.Add(buttonUpdateNow);
            panel.Children.Add(buttonUpdateLater);

            buttonUpdateNow.Focus();

            this.LayoutRoot.Children.Add(textBlock);
            this.LayoutRoot.Children.Add(panel);
        }

        private void AddMessageControls(string message)
        {
            this.ClearLayoutRoot();

            TextBlock textBlock = this.GetTextBlock(message, 0);
            textBlock.TextWrapping = TextWrapping.Wrap;

            Button button = this.GetButton("OK", 1);
            button.Click += (object sender, RoutedEventArgs e) =>
                {
                    this.HideControl();
                };

            this.LayoutRoot.Children.Add(textBlock);
            this.LayoutRoot.Children.Add(button);
        }

        private Button GetButton(string content, int gridRow)
        {
            Button button = new Button();
            button.Width = 70;
            button.Height = 30;
            button.Style = (Style)FindResource("ButtonLightStyle");
            button.Content = content;
            button.SetValue(Grid.RowProperty, gridRow);
            button.Margin = new Thickness(10, 10, 10, 10);
            button.FocusVisualStyle = null;

            return button;
        }

        private StackPanel GetStackPanel()
        {
            StackPanel panel = new StackPanel();
            panel.SetValue(Grid.RowProperty, 1);
            panel.Orientation = Orientation.Horizontal;
            panel.HorizontalAlignment = HorizontalAlignment.Center;
            panel.VerticalAlignment = VerticalAlignment.Center;

            return panel;
        }

        private TextBlock GetTextBlock(string text, int gridRow)
        {
            TextBlock textBlock = new TextBlock();
            textBlock.Foreground = Brushes.Black;
            textBlock.Text = text;
            textBlock.HorizontalAlignment = HorizontalAlignment.Center;
            textBlock.VerticalAlignment = VerticalAlignment.Center;
            textBlock.SetValue(Grid.RowProperty, gridRow);

            return textBlock;
        }

        private void HideControl()
        {
            this.hideControlStoryboard.Begin();
        }

        #endregion
    }
}
